﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

using ScrumTable.Common;
using ScrumTable.Common.Monitoring;
using ScrumTable.Common.Threading;
using ScrumTable.DL.Data.Generic.Cache;
using ScrumTable.DL.Data.Schema.ScrumTableV1;
using ScrumTable.DL.Data.TFS.Services.TeamFoundation;

#endregion

namespace ScrumTable.DL.Data.TFS.Bulk.Services.IterationHandling
{
    /// <summary>
    /// This class is used to load all data below a given iteration.
    /// </summary>
    internal class TfsIterationLoader : IterationLoaderBase
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        internal const string IterationWorkItemIdKey = "IterationWorkItemId";

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new TfsIterationLoader with the associated schema config.
        /// </summary>
        /// <param name="schema">Specifies the schema configuration data.</param>
        internal TfsIterationLoader(IterationSchema schema)
            : base(schema)
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Loads the iteration data using the strategies of the underlying
        /// derivations.
        /// </summary>
        /// <param name="importMonitor">Specifies the data load monitor which is used to visualize the data import.</param>
        /// <param name="workItemDataLoader">Specifies the work item loader which is used to initialize the CacheWorkItem instances.</param>
        /// <param name="context">Specifies the context with all data which is required to import the data.</param>
        /// <param name="callback">Specifies the callback mechanism method which is used to call the next load part.</param>
        internal override void StartLoadIterationData(IDataImportMonitor importMonitor, WorkItemDataLoader workItemDataLoader, InitialDataCacheLoader.DataLoadContext context, Action<Exception> callback)
        {
            PreCondition.AssertNotNull(importMonitor, "importMonitor");
            PreCondition.AssertNotNull(workItemDataLoader, "workItemDataLoader");
            PreCondition.AssertNotNull(context, "context");
            PreCondition.AssertNotNull(callback, "callback");

            IDictionary<string, WorkItem> loadedItems = new Dictionary<string, WorkItem>();
            AsyncDataLoader<WorkItemDataLoadToken<WorkItem>> loader = new AsyncDataLoader<WorkItemDataLoadToken<WorkItem>>(
                this,
                importMonitor,
                IterationLoadPart,

                /* callback method if completed */
                result => ProcessIterationLoadCallback(importMonitor, context, callback, ((DataResult)result).Error, loadedItems));

            IDictionary<int, WorkItemDataLoadToken<WorkItem>> loadTokens = workItemDataLoader.QueryWorkItemTokens<WorkItem>(
                context.TfsContext.WorkItems,
                context.CacheProject,
                context.CacheContext.Schema.IterationSchema.SchemaMappedName);

            loader.LoadAsync(
                /* contains the tokens with the information about the data to load */
                loadTokens.Values,

                /* async part, should contain long running process; force load all data */
                t => t.Result = context.TfsContext.WorkItems.GetWorkItem(t.Reference),

                /* context sync part, should only contain an import/assignment  */
                t =>
                {
                    if (t.Result != null)
                    {
                        string nameValue = t.Result.TryGetValue(
                            context.CacheContext.Schema.IterationSchema.Name,
                            string.Empty);

                        if (!string.IsNullOrEmpty(nameValue))
                        {
                            loadedItems[nameValue] = t.Result;
                        }
                    }
                });
        }

        private void ProcessIterationLoadCallback(IDataImportMonitor importMonitor, InitialDataCacheLoader.DataLoadContext context, Action<Exception> callback, Exception error, IDictionary<string, WorkItem> loadedItems)
        {
            if (error == null)
            {
                try
                {
                    LoadIterationData(context, loadedItems);
                    callback(null);
                }
                catch (Exception e)
                {
                    importMonitor.EndSubProgress(IterationLoadPart, e);
                    callback(e);
                }
            }
            else
            {
                // abort iteration load and notify the caller about the error
                callback(error);
            }
        }

        private void LoadIterationData(InitialDataCacheLoader.DataLoadContext context, IDictionary<string, WorkItem> loadedItems)
        {
            List<Node> iterationNodes = new List<Node>();
            LoadIterationsRecursive(iterationNodes, new Dictionary<string, Node>(), context.TfsProject.IterationRootNodes);

            double iterationIndex = 0;

            foreach (Node iteration in iterationNodes)
            {
                WorkItem assignedWorkItem = loadedItems.ContainsKey(iteration.Path)
                    ? loadedItems[iteration.Path]
                    : null;

                string iterationPathName = GetIterationPathName(iteration.Path);
                CacheIteration cacheIteration = context.CacheContext.ElementFactory.CreateIteration(
                    ObjectUtil.ConvertToString(iteration.Id),
                    iteration.Id,
                    iterationIndex,
                    iterationPathName,
                    iteration.Path,
                    (assignedWorkItem != null) ? assignedWorkItem.TryGetValue(context.CacheContext.Schema.IterationSchema.StartTime, DateTime.Now) : DateTime.Now,
                    (assignedWorkItem != null) ? assignedWorkItem.TryGetValue(context.CacheContext.Schema.IterationSchema.EndTime, DateTime.Now) : DateTime.Now);

                // store assigned workitem id -> it is used to store the values back into the TFS
                cacheIteration.Attributes[IterationWorkItemIdKey] = (assignedWorkItem != null) ? assignedWorkItem.Id : CacheIdentity.DefaultId;

                context.LoadedIterations.Add(cacheIteration);
                ++iterationIndex;
            }
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}